spb/satelliteindex
Public
TypeScript 66.5%
Python 30.9%
JavaScript 1.4%
CSS 0.7%
1import type { Metadata } from 'next';2import Link from 'next/link';3import { notFound } from 'next/navigation';4import { AdminError, AdminHeader, fmtBytes } from '@/components/admin/ui';5import { KeyValues } from '@/components/meta/prose';6import { adminApi, AdminApiError } from '@/lib/admin-api';7import { fmtDateTime, fmtInt } from '@/lib/format';89export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise<Metadata> {10 const { id } = await params;11 return { title: `Raw ${id}` };12}1314export default async function AdminRawRecordPage({ params }: { params: Promise<{ id: string }> }) {15 const { id } = await params;16 let record;17 try {18 record = (await adminApi.rawRecord(id)).data;19 } catch (e) {20 if (e instanceof AdminApiError && e.notFound) notFound();21 return (22 <>23 <AdminHeader title="Raw record" />24 <AdminError message={(e as Error).message} />25 </>26 );27 }28 return (29 <>30 <AdminHeader31 title={<span className="mono break-all">{record.source_native_id ?? record.id}</span>}32 lede={`${record.connector_name} · fetched ${fmtDateTime(record.fetched_at)}`}33 action={34 <Link href={`/admin/raw?connector=${encodeURIComponent(record.connector_name)}`} className="text-sm text-accent hover:underline">35 ← Raw records36 </Link>37 }38 />39 <KeyValues40 rows={[41 { k: 'Record id', v: <span className="mono text-xs">{record.id}</span> },42 { k: 'Run', v: record.run_id ? <Link href={`/admin/connectors/${encodeURIComponent(record.connector_name)}`} className="mono text-xs link">{record.run_id}</Link> : '—' },43 { k: 'Source', v: <span className="mono text-xs">{record.source_id}</span> },44 { k: 'Source URL', v: record.source_url ? <a href={record.source_url} rel="noopener noreferrer" target="_blank" className="link mono break-all text-xs">{record.source_url}</a> : '—' },45 { k: 'Storage path', v: <span className="mono break-all text-xs">{record.storage_path ?? '— (payload not kept: unchanged upstream)'}</span> },46 { k: 'Content type', v: <span className="mono text-xs">{record.content_type ?? '—'}</span> },47 { k: 'Size / records', v: `${fmtBytes(record.byte_size)} · ${fmtInt(record.record_count)} records` },48 { k: 'Payload hash', v: <span className="mono break-all text-xs">{record.payload_hash ?? '—'}</span> },49 { k: 'Processing', v: <span className={record.processing_status === 'processed' ? 'text-active' : record.processing_status === 'failed' ? 'text-danger' : ''}>{record.processing_status ?? '—'}{record.processed_at ? ` · ${fmtDateTime(record.processed_at)}` : ''}</span> },50 ...(record.error ? [{ k: 'Error', v: <span className="mono text-xs text-danger">{record.error}</span> }] : []),51 ]}52 />5354 <div className="mt-6 flex items-end justify-between gap-3">55 <h2 className="text-base font-semibold tracking-tight md:text-lg">Payload preview</h2>56 {record.truncated && <span className="rounded border border-warn/40 bg-warn-soft px-2 py-0.5 text-xs text-warn">truncated at 200 KB</span>}57 </div>58 {record.preview === null ? (59 <p className="mt-3 rounded-md border border-dashed border-rule-strong px-4 py-8 text-center text-sm text-ink-3">Payload file unavailable on this host (no storage path or file missing under SI_DATA_DIR/raw)</p>60 ) : (61 <pre className="scrollbar-thin mt-3 max-h-[70vh] overflow-auto rounded-md border border-rule bg-plane-2 p-3 text-[12px] leading-relaxed text-ink">62 <code className="mono">{record.preview}</code>63 </pre>64 )}65 </>66 );67}68